#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Web presentation Data classes"""

#from django import template
#from django import http

from django.shortcuts import get_object_or_404, redirect
#                   render_to_response, get_list_or_404, redirect, HttpResponse
from django.utils.translation import ugettext as _
#from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_control
#from django.views.decorators.cache import never_cache

#from django.views.decorators.csrf import csrf_protect
from django.contrib.auth.decorators import login_required
#from django.forms import models as model_forms
from django.contrib.admin.util import get_deleted_objects
from django.contrib.admin.sites import AdminSite
from django.db import router

from django.contrib import messages
from django.contrib.auth.models import Group
#from django.db import transaction
from django.db.models import Q
#from copy import copy
from django.views.generic import (ListView, DetailView, DeleteView)
#                                  CreateView, UpdateView,
import datetime

# pylint: disable=W0611
from .views import (DecoratorChainingMixin, BaseCreateView,
                    BaseUpdateView, class_by_name, general_context, restore,
                    desktoplist)

#------------------------------------------------------------------------------
def use_obj_read(usr, obj_id):
    "Return True if use has at least one permission to read"

    can_read = False
    gr_ids = [el.group_id for el in
                        class_by_name('DataGroup').objects.filter(data=obj_id)]
    pub_id = Group.objects.get(name='Public').id # pylint: disable=E1101
    qrs = class_by_name('UserGroupPerm').objects.filter(
                                Q(user=usr.id), Q(group__in=gr_ids),
                                Q(read=True) | Q(write=True) | Q(master=True))

    #if data in 'Public' group, all users can read it, i.e. pub_id in gr_ids
    #or data at least in one group, where user can read/write/master
    if (pub_id in gr_ids) or usr.is_superuser or qrs.exists():
        can_read = True

    return can_read

#------------------------------------------------------------------------------
def use_obj_write(usr, obj_id):
    "Return True if user has at least one permission to write"

    can_write = False
    gr_ids = [el.group_id for el in
                        class_by_name('DataGroup').objects.filter(data=obj_id)]
    qrs = class_by_name('UserGroupPerm').objects.filter(
                                        Q(user=usr.id), Q(group__in=gr_ids),
                                        Q(write=True) | Q(master=True))
    if usr.is_superuser or qrs.exists():
        can_write = True

    return can_write

#------------------------------------------------------------------------------
def use_obj_master(usr, obj_id):
    "Return True if user has master permission"

    master = False
    gr_ids = [el.group_id for el in
                        class_by_name('DataGroup').objects.filter(data=obj_id)]
    qrs = class_by_name('UserGroupPerm').objects.filter(group__in=gr_ids,
                                                        user=usr.id,
                                                        master=True)
    if qrs.exists():
        master = True

    return master

#------------------------------------------------------------------------------
def datalist_create(request, genlist):
    """
    Form  data list for rendering it on DataList and Seach pages. Data list has
    the follwing structure: 'dataname - ondesktop' or 'dataname - checkbox' or
    'dataname - no edit permission'.
    """

    dt_list = []

    for elem in genlist:
        real_obj = get_object_or_404(class_by_name(elem.classname_id),
                                     pk=elem.id)
        if class_by_name('DeskTop').objects.filter(base=elem,
                                                   user=request.user).exists():
            dt_list.append((real_obj, 'on desktop'))
        elif use_obj_read(request.user, elem.id):
            dt_list.append((real_obj, 'checkbox'))
        else:
            dt_list.append((real_obj, 'no edit permission'))
    return dt_list

#------------------------------------------------------------------------------
#@csrf_protect
#@transaction.atomic
@login_required
@cache_control(must_revalidate=True, max_age=3600)
def markdelete(request, fmname, pk): # pylint: disable=C0103
    "Data and all links with this data mark as deleted"

    obj_list = [elem for elem in class_by_name(fmname).objects.filter(pk=pk)]
    obj_list.extend([elem for elem in
                     class_by_name('Link').objects.filter(Q(subject=pk) |
                                                          Q(property=pk) |
                                                          Q(link_type=pk))])
    #BEGIN
    try:
        for elem in obj_list:
            elem.deleted_on = datetime.datetime.now()
            elem.deleted_by_id = request.user.username
            elem.save()
            # and delete the object from all desktops
            class_by_name('DeskTop').objects.filter(base=elem).delete()
    except StandardError:
        import sys
        messages.add_message(request, messages.ERROR,
        _('DataGroup data creation ERROR: ') + str(sys.exc_info()[1]))
        #ROLLBACK and exit
    #COMMIT

    messages.add_message(request, messages.INFO,
                        _(u"%s data and its links are marked as "
                          "deleted." % fmname))

    return redirect('/base/' + pk + '/')

#------------------------------------------------------------------------------
#def subj_prop_inlink(obj, details_list, desktop_list):
def subj_prop_inlink(obj, details_list):
    """
        Replace Subj/Property 2-element tuple by 3-element in show-detail-list
        if Subj/Property is None
    """

    for elem in details_list:
        if elem[0] == 'Subject' and not elem[1]:
            details_list[details_list.index(elem)] = (elem[0], elem[1],
                                                    obj.link_type.classname1)
        if elem[0] == 'Property' and not elem[1]:
            details_list[details_list.index(elem)] = (elem[0], elem[1],
                                                    obj.link_type.classname2)
    return details_list

#------------------------------------------------------------------------------
# pylint: disable=R0901
class DataListView(DecoratorChainingMixin, ListView):
    'Form generator class for lists of data entries'


    context_object_name = "data_list"
    template_name = "data_list.html"

    def get_queryset(self):

        return datalist_create(self.request, # pylint: disable=E1101
                            class_by_name('Base').objects.all())

    def get_context_data(self, **kwargs):

        context = super(self.__class__, self).get_context_data(**kwargs)
        context.update(general_context(self.request,  # pylint: disable=E1101
                                       'Data List', 'Data List'))
        context.update({'action': '/adddesktop/DeskTop/'})
        context['title'] = _('Data')
        return context
# pylint: enable=R0901

#------------------------------------------------------------------------------
# pylint: disable=R0901
class DataDetailView(DecoratorChainingMixin, DetailView):
    'Form generator for detailed view of data entry'


    context_object_name = "data"
    template_name = "data_details.html"
    model = class_by_name('Base')

    def get_object(self, queryset=None):

        obj = class_by_name('Base').objects.get(
                                  pk=self.kwargs['pk']) # pylint: disable=E1101
        real_obj = get_object_or_404(class_by_name(obj.classname_id),
                                  pk=self.kwargs['pk']) # pylint: disable=E1101
        return real_obj

    def get_context_data(self, **kwargs):

        context = super(self.__class__, self).get_context_data(**kwargs)
        ind = self.kwargs['pk'] # pylint: disable=E1101
        classname = self.object.classname.classname
        #groups where data is
        ind_in_grops = [el.group.name for el in class_by_name(
                                        'DataGroup').objects.filter(data=ind)]
        data_details = self.object.show_details()
        if ind_in_grops:
            groups_str = ', '.join(ind_in_grops)
            data_details.append(('Groups', groups_str))

        context.update(general_context(self.request, # pylint: disable=E1101
                               'Data details',
                               "View %s data (id=%s)" % (classname,
                                    self.kwargs['pk']))) # pylint: disable=E1101
        if classname == 'Link':
            subj_prop_inlink(self.object, data_details)
           #subj_prop_inlink(self.object, data_details, context['desktop_list'])


#TODO: rendering of subject_list&property_list isolate in a different  function
        subject_list = [(elem, class_by_name(elem.property.classname_id
                                       ).objects.get(id=elem.property_id) if
                                                    elem.property else None)
                            for elem in class_by_name('Link').objects.filter(
                                        subject=ind) if not elem.deleted_on]

        property_list = [(elem, class_by_name(elem.subject.classname_id
                                        ).objects.get(id=elem.subject_id) if
                                                    elem.subject else None)
                        for elem in class_by_name('Link').objects.filter(
                                        property=ind) if not elem.deleted_on]
#---

        delvalue = None
        if self.object.deleted_by:
            butvalue = _('Undelete')
# pylint: disable=E1101
            if self.request.user.is_superuser or use_obj_master(
                                            self.request.user, self.object.id):
# pylint: enable=E1101
                delvalue = _('Destroy')
        else:
            butvalue = _('Edit')
            delvalue = _('Delete')

# pylint: disable=E1101
        context.update({'can_write': use_obj_write(self.request.user,
                                                 self.kwargs['pk']),
                        'can_read': use_obj_read(self.request.user,
                                                 self.kwargs['pk']),
# pylint: enable=E1101
                        'butvalue': butvalue,
                        'delvalue': delvalue,
                        'subject_list': subject_list,
                        'property_list': property_list,
                        'user_action': '/%s/%s/' % (classname, ind),
                        'data': data_details,
                        })
        return context
# pylint: disable=R0901

#------------------------------------------------------------------------------
# pylint: disable=W0232,R0904
class DataCreateView(DecoratorChainingMixin, BaseCreateView):
    'Generate forms for creating new data items'

    template_name = 'add_data.html'
# pylint: enable=W0232,R0904


#------------------------------------------------------------------------------
# pylint: disable=W0232,R0903,R0904
class DataUpdateView(DecoratorChainingMixin, BaseUpdateView):
    'Forms for editing a data item'

    template_name = 'add_data.html'
# pylint: enable=W0232,R0903,R0904


#------------------------------------------------------------------------------
# pylint: disable=R0901
class DataDeleteView(DecoratorChainingMixin, DeleteView):
    'Forms for editing and deleting'


    template_name = 'data_confirm_delete.html'

    def get_success_url(self):
        'Prepare the next page url and  messsage'

        messages.add_message(self.request, # pylint: disable=E1101
                        messages.INFO,
                        _(u"%s data %s was successfully deleted!"
                             %(self.kwargs['fmname'], # pylint: disable=E1101
                               self.object)))
        url = '/datalist/'
        return url

    def get_object(self, queryset=None):

# pylint: disable=E1101
        obj = get_object_or_404(class_by_name(self.kwargs['fmname']),
                                pk=self.kwargs['pk'])
# pylint: enable=E1101
        return obj

    def get_context_data(self, **kwargs):

        context = super(self.__class__, self).get_context_data(**kwargs)

        #Find related objects
        model = class_by_name(self.kwargs['fmname']) # pylint: disable=E1101
        opts = model._meta # pylint: disable=W0212
        object_name = opts.verbose_name
        admin_site = AdminSite('admin', 'data')
        using = router.db_for_write(model)
        #should be list of QuerySet, objs=[self.get_object] is bound method
        objs = [get_object_or_404(model,
                    pk=self.kwargs['pk'])] # pylint: disable=E1101
        #deleted_objects, perms_needed, protected = get_deleted_objects(...)
        deleted_objects = get_deleted_objects(objs, opts,
                                    self.request.user, # pylint: disable=E1101
                                    admin_site, using)[0]

        context.update({'title': _('Delete data'),
                        'content_title': _(u"Delete data %s" % (object_name)),
                        'object_name': object_name,
                        'deleted_objects': deleted_objects,})

        return context
# pylint: enable=R0901
